package spring.beans.factory;

import spring.annotation.Autowired;
import spring.beans.BeanFactoryAware;
import spring.beans.BeanNameAware;
import spring.beans.InitializingBean;
import spring.beans.config.BeanDefinition;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory {

    /**
     * 创建bean实例
     * @param beanName
     * @param beanDefinition
     * @return
     */
    protected Object createBean(String beanName, BeanDefinition beanDefinition) {

        if (containsSingleton(beanName)){
            return getSingleton(beanName);
        }

        Class clazz = beanDefinition.getClazz();

        try {
            // 根据类型创建实例
            Object instance = clazz.getDeclaredConstructor().newInstance();

            populateBean(clazz, instance);

            invokeAwareMethods(beanName,instance);

            instance = applyBeanPostProcessorsBeforeInitialization(beanName,instance);

            invokeInitMethods(instance);

            instance = applyBeanPostProcessorsAfterInitialization(beanName,instance);

            // 返回实例
            return instance;

        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 依赖注入
     * @param clazz
     * @param instance
     * @throws IllegalAccessException
     */
    protected void populateBean(Class clazz, Object instance) throws IllegalAccessException {
        for (Field field : clazz.getDeclaredFields()){
            if(field.isAnnotationPresent(Autowired.class)){
                Object bean = getBean(field.getName());
                field.setAccessible(true);
                field.set(instance, bean);
            }
        }
    }

    /**
     * aware回调
     * @param beanName
     * @param instance
     */
    protected abstract void invokeAwareMethods(String beanName, Object instance);

    /**
     * BeanPostProcessor前置处理器
     * @param beanName
     * @param instance
     * @return
     */
    protected abstract Object applyBeanPostProcessorsBeforeInitialization(String beanName, Object instance) throws Exception;

    /**
     * 初始化
     * @param instance
     * @throws Exception
     */
    protected void invokeInitMethods(Object instance) throws Exception {
        if (instance instanceof InitializingBean) {
            ((InitializingBean) instance).afterPropertiesSet();
        }
    }
    /**
     * BeanPostProcessor后置处理器
     * @param beanName
     * @param instance
     * @return
     */
    protected abstract Object applyBeanPostProcessorsAfterInitialization(String beanName, Object instance);

}
